//
//  DBSphereView.m
//  sphereTagCloud
//
//  Created by Xinbao Dong on 14/8/31.
//  Copyright (c) 2014年 Xinbao Dong. All rights reserved.
//


#import "DBSphereView.h"
#import "DBMatrix.h"
#import "DrawLineView.h"

#import "BGSafeMutableArray.h"

@interface DBSphereView() <UIGestureRecognizerDelegate>
{
    DBPoint normalDirection;
    CGPoint last;
    
    CGFloat velocity;
    
    CADisplayLink *timer;
    CADisplayLink *inertia;
    
    DrawLineView *lineBgView;
    
    dispatch_queue_t queue;
    
    CGFloat point1x;
    CGFloat point1y;
    CGFloat point2x;
    CGFloat point2y;
    
    NSValue *lineValue1;
    NSValue *lineValue2;
    
    DBPoint lineP1;
    DBPoint lineP2;
}
@property (nonatomic, strong) NSMutableArray *tags;
//@property (nonatomic, strong) NSMutableArray *coordinate;
@property (nonatomic, strong) BGSafeMutableArray *coordinate;
@property (nonatomic, strong) NSMutableArray *linePointsArray;
@property (nonatomic, strong) NSMutableArray *linesArray;
@end

@implementation DBSphereView

- (id)initWithCoder:(NSCoder*)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.angle = 0.02;
        [self initUI];
    }
    return self;
}
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.angle = 0.05;
        [self initUI];
    }
    return self;
}
- (void)initUI {
    self.backgroundColor = COLOR_OF_CLEAR;
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    
    lineBgView = [[DrawLineView alloc] initWithFrame:self.bounds];
    [self addSubview:lineBgView];
    lineBgView.userInteractionEnabled = YES;
    
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self addGestureRecognizer:gesture];
    
    inertia = [CADisplayLink displayLinkWithTarget:self selector:@selector(inertiaStep)];
    inertia.frameInterval = 3;
    [inertia addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(autoTurnRotation)];
    timer.frameInterval = 3;
    [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
#pragma mark - initial set
- (void)setCloudTags:(NSArray *)array {
    self.tags = [NSMutableArray arrayWithArray:array];
//    self.coordinate = [[NSMutableArray alloc] initWithCapacity:0];
    self.coordinate = [[BGSafeMutableArray alloc] init];
    self.linePointsArray = [NSMutableArray arrayWithCapacity:0];
    self.linesArray = [NSMutableArray arrayWithCapacity:0];
    
    CGFloat p1 = M_PI * (3 - sqrt(5));
    CGFloat p2 = 2. / self.tags.count;
    
    for (NSInteger i = 0; i < self.tags.count; i ++) {
        CGFloat y = i * p2 - 1 + (p2 / 2);
        CGFloat r = sqrt(1 - y * y);
        CGFloat p3 = i * p1;
        CGFloat x = cos(p3) * r;
        CGFloat z = sin(p3) * r;
        NSLog(@"x=%f, y=%f, z=%f\n", x, y, z);
        
        DBPoint point = DBPointMake(x, y, z);
        NSValue *value = [NSValue value:&point withObjCType:@encode(DBPoint)];
//        [self.coordinate addObject:value];
//        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
            [self.coordinate addObject:value];
        });
        
        CGFloat time = (arc4random() % 10 + 10.) / 20.;
        [UIView animateWithDuration:time delay:0. options:UIViewAnimationOptionCurveEaseOut animations:^{
            [self setTagOfPoint:point andIndex:i];
        } completion:nil];
    }
    
    NSMutableArray *tempArr6 = [NSMutableArray arrayWithCapacity:0];
    for (NSInteger i = 0; i < self.tags.count; i++) {
        NSMutableArray *gouzaoArr = [NSMutableArray arrayWithCapacity:0];
        
        for (NSInteger j = 0; j<self.tags.count; j++) {
            if (i == j) {
                continue;
            }
            
            NSValue *value1 = [self.coordinate objectAtIndex:i];
            DBPoint p1;
            [value1 getValue:&p1];
            
            NSValue *value2 = [self.coordinate objectAtIndex:j];
            DBPoint p2;
            [value2 getValue:&p2];
            
            long double d = pow(p1.x-p2.x, 2)+pow(p1.y-p2.y, 2)+pow(p1.z-p2.z, 2);
            DBLinePoints linePoints = DBLinePointsMake(i, j);
            NSValue *value = [NSValue value:&linePoints withObjCType:@encode(DBLinePoints)];
            
            NSDictionary *dealWithDic = @{@"d":[NSString stringWithFormat:@"%@", [NSNumber numberWithDouble:d]], @"v":value};
            [gouzaoArr addObject:dealWithDic];
        }
        
        NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"d" ascending:YES];//yes升序排列，no,降序排列
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:&sorter count:1];
        NSArray *returnArray = [gouzaoArr sortedArrayUsingDescriptors:sortDescriptors];//注意这里的returnArray进行排序后会生产一个新的数组指针，不能在用tempArray,tempArray还是保持不变的。
        
        for (NSInteger i = 0; i < 6; i++) {
            NSDictionary *dic = returnArray[i];
            [tempArr6 addObject:dic[@"v"]];
        }
    }
    if(tempArr6.count == 0){
        return;
    }
    for (NSInteger i = 0; i < tempArr6.count-1; i++) {
        NSValue *value1 = tempArr6[i];
        DBLinePoints lps1;
        [value1 getValue:&lps1];
        BOOL chongfuFlag = NO;
        for (NSInteger j = i+1; j < tempArr6.count; j++) {
            NSValue *value2 = tempArr6[j];
            DBLinePoints lps2;
            [value2 getValue:&lps2];
            
            if (((lps1.p1 == lps2.p1) && (lps1.p2 == lps2.p2)) || ((lps1.p1 == lps2.p2) && (lps1.p2 == lps2.p1))) {
                chongfuFlag = YES;
                break;
            }
        }
        if (!chongfuFlag) {
            [self.linePointsArray addObject:value1];
        }
    }
    NSValue *v1Last = tempArr6.lastObject;
    DBLinePoints lps1Last;
    [v1Last getValue:&lps1Last];
    BOOL chongfuFlag = NO;
    for (NSInteger i = 0; i < tempArr6.count-1; i++) {
        NSValue *v2Last = tempArr6[i];
        DBLinePoints lps2Last;
        [v2Last getValue:&lps2Last];
        
        if (((lps1Last.p1 == lps2Last.p1) && (lps1Last.p2 == lps2Last.p2)) || ((lps1Last.p1 == lps2Last.p2) && (lps1Last.p2 == lps2Last.p1))) {
            chongfuFlag = YES;
            break;
        }
    }
    if (!chongfuFlag) {
        [self.linePointsArray addObject:v1Last];
    }
    
    NSInteger a = arc4random() % 10 - 5;
    NSInteger b = arc4random() % 10 - 5;
    normalDirection = DBPointMake(a, b, 0);
    [self timerStart];
}
#pragma mark - set frame of point
- (void)updateFrameOfPoint:(NSInteger)index direction:(DBPoint)direction andAngle:(CGFloat)angle {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(queue, ^{
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        NSValue *value = [strongSelf.coordinate objectAtIndex:index];
        DBPoint point;
        [value getValue:&point];
        
        DBPoint rPoint = DBPointMakeRotation(point, direction, angle);
        value = [NSValue value:&rPoint withObjCType:@encode(DBPoint)];
//       strongSelf.coordinate[index] = value;
        [strongSelf.coordinate replaceObjectAtIndex:index withObject:value];
        dispatch_async(dispatch_get_main_queue(), ^{
            // GCD 回主线程，刷新UI
            [self setTagOfPoint:rPoint andIndex:index];
        });
    });
    
//    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//        // GCD 异步子线程
//       __strong __typeof(weakSelf)strongSelf = weakSelf;
//       NSValue *value = [strongSelf.coordinate objectAtIndex:index];
//       DBPoint point;
//       [value getValue:&point];
//
//       DBPoint rPoint = DBPointMakeRotation(point, direction, angle);
//       value = [NSValue value:&rPoint withObjCType:@encode(DBPoint)];
////       strongSelf.coordinate[index] = value;
//       [strongSelf.coordinate replaceObjectAtIndex:index withObject:value];
//       dispatch_async(dispatch_get_main_queue(), ^{
//           // GCD 回主线程，刷新UI
//           [self setTagOfPoint:rPoint andIndex:index];
//       });
//    });
}
- (void)setTagOfPoint:(DBPoint)point andIndex:(NSInteger)index {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        // GCD 回主线程，刷新UI
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        UIView *view = [strongSelf.tags objectAtIndex:index];
        view.center = CGPointMake((point.x+1)*(self.frame.size.width*0.5), (point.y+1)*(self.frame.size.width*0.5));
        
        CGFloat transform = (point.z + 2) / 3;
        view.transform = CGAffineTransformScale(CGAffineTransformIdentity, transform, transform);
        view.layer.zPosition = transform;
        view.alpha = transform;
        if (point.z < 0) {
            view.userInteractionEnabled = NO;
        } else {
            view.userInteractionEnabled = YES;
        }
    });
}
- (void)setTagOfLinePoints:(DBLinePoints)lps {
//    NSValue *value1 = [self.coordinate objectAtIndex:lps.p1];
//    DBPoint p1;
//    [value1 getValue:&p1];
//
//    NSValue *value2 = [self.coordinate objectAtIndex:lps.p2];
//    DBPoint p2;
//    [value2 getValue:&p2];
    
//    CGFloat point1x = (p1.x+1)*(self.frame.size.width*0.5);
//    CGFloat point1y = (p1.y+1)*self.frame.size.width*0.5;
//
//    CGFloat point2x = (p2.x+1)*(self.frame.size.width*0.5);
//    CGFloat point2y = (p2.y+1)*self.frame.size.width*0.5;
    
    lineValue1 = [self.coordinate objectAtIndex:lps.p1];
    [lineValue1 getValue:&lineP1];

    lineValue2 = [self.coordinate objectAtIndex:lps.p2];
    [lineValue2 getValue:&lineP2];
    
    point1x = (lineP1.x+1)*(self.frame.size.width*0.5);
    point1y = (lineP1.y+1)*self.frame.size.width*0.5;
    
    point2x = (lineP2.x+1)*(self.frame.size.width*0.5);
    point2y = (lineP2.y+1)*self.frame.size.width*0.5;

    BOOL zheng = YES;
    if (lineP1.z<0 || lineP2.z<0) {
        zheng = NO;
    }
    
//    [self drawLine:point1 point2:point2 zheng:zheng];
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    [linePath moveToPoint:CGPointMake(point1x, point1y)];
    [linePath addLineToPoint:CGPointMake(point2x, point2y)];
    shapeLayer.path = linePath.CGPath;
    shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
    shapeLayer.strokeColor = RGB(255, 220, 100).CGColor;
    shapeLayer.lineWidth = zheng?1:0.5;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    [lineBgView.lines addObject:shapeLayer];
}
- (void)drawLine:(CGPoint)p1 point2:(CGPoint)p2 zheng:(BOOL)zheng {
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    [linePath moveToPoint:CGPointMake(p1.x, p1.y)];
    [linePath addLineToPoint:CGPointMake(p2.x, p2.y)];
    shapeLayer.path = linePath.CGPath;
    shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
    shapeLayer.strokeColor = RGB(255, 220, 100).CGColor;
    shapeLayer.lineWidth = zheng?1:0.5;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    [lineBgView.lines addObject:shapeLayer];
}


#pragma mark - autoTurnRotation
- (void)timerStart {
    if (timer) {
        timer.paused = NO;
    }
}
- (void)timerStop {
    if (timer) {
        timer.paused = YES;
    }
}
- (void)autoTurnRotation {
    for (NSInteger i = 0; i < self.tags.count; i ++) {
        [self updateFrameOfPoint:i direction:normalDirection andAngle:self.angle];
    }
    
    [lineBgView clearScreen];
    for (NSInteger i = 0; i < self.linePointsArray.count; i++) {
        NSValue *value = self.linePointsArray[i];
        
        DBLinePoints lps;
        [value getValue:&lps];
        
        [self setTagOfLinePoints:lps];
    }
    [lineBgView setNeedsDisplay];
}
#pragma mark - inertia
- (void)inertiaStart {
    [self timerStop];
    if (inertia) {
        inertia.paused = NO;
    }
}
- (void)inertiaStop {
    [self timerStart];
    if (inertia) {
        inertia.paused = YES;
    }
}
- (void)inertiaStep {
    if (velocity <= 0) {
        [self inertiaStop];
    } else {
        velocity -= 50.;
        CGFloat angle = velocity / self.frame.size.width * 2. * inertia.duration;
        for (NSInteger i = 0; i < self.tags.count; i ++) {
            [self updateFrameOfPoint:i direction:normalDirection andAngle:angle];
        }
        
        [lineBgView clearScreen];
        for (NSInteger i = 0; i < self.linePointsArray.count; i++) {
            NSValue *value = self.linePointsArray[i];
            DBLinePoints lps;
            [value getValue:&lps];

            [self setTagOfLinePoints:lps];
        }
        [lineBgView setNeedsDisplay];
    }
}
#pragma mark - gesture selector
- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateBegan) {
        last = [gesture locationInView:self];
        [self timerStop];
        [self inertiaStop];
    } else if (gesture.state == UIGestureRecognizerStateChanged) {
        CGPoint current = [gesture locationInView:self];
        DBPoint direction = DBPointMake(last.y - current.y, current.x - last.x, 0);
        CGFloat distance = sqrt(direction.x * direction.x + direction.y * direction.y);
        CGFloat angle = distance / (self.frame.size.width / 2.);
        
        for (NSInteger i = 0; i < self.tags.count; i++) {
            [self updateFrameOfPoint:i direction:direction andAngle:angle];
        }
        
        [lineBgView clearScreen];
        for (NSInteger i = 0; i < self.linePointsArray.count; i++) {
            NSValue *value = self.linePointsArray[i];
            DBLinePoints lps;
            [value getValue:&lps];
            
            [self setTagOfLinePoints:lps];
        }
        [lineBgView setNeedsDisplay];
        
        normalDirection = direction;
        last = current;
    } else if (gesture.state == UIGestureRecognizerStateEnded) {
        CGPoint velocityP = [gesture velocityInView:self];
        velocity = sqrt(velocityP.x * velocityP.x + velocityP.y * velocityP.y);
        [self inertiaStart];
    }
}

@end











/*
#import "DBSphereView.h"
#import "DBMatrix.h"

@interface DBSphereView() <UIGestureRecognizerDelegate>

@end

@implementation DBSphereView
{
    NSMutableArray *tags;
    NSMutableArray *coordinate;
    DBPoint normalDirection;
    CGPoint last;
    
    CGFloat velocity;
    
    CADisplayLink *timer;
    CADisplayLink *inertia;
}

- (void)setup {
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self addGestureRecognizer:gesture];
    
    inertia = [CADisplayLink displayLinkWithTarget:self selector:@selector(inertiaStep)];
    [inertia addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(autoTurnRotation)];
    [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

- (id)initWithCoder:(NSCoder*)aDecoder
{
    if(self = [super initWithCoder:aDecoder]) {
        [self setup];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

#pragma mark - initial set

- (void)setCloudTags:(NSArray *)array
{
    tags = [NSMutableArray arrayWithArray:array];
    coordinate = [[NSMutableArray alloc] initWithCapacity:0];
    
    for (NSInteger i = 0; i < tags.count; i ++) {
        UIView *view = [tags objectAtIndex:i];
        view.center = CGPointMake(self.frame.size.width / 2., self.frame.size.height / 2.);
    }
    
    CGFloat p1 = M_PI * (3 - sqrt(5));
    CGFloat p2 = 2. / tags.count;
    
    for (NSInteger i = 0; i < tags.count; i ++) {
        
        CGFloat y = i * p2 - 1 + (p2 / 2);
        CGFloat r = sqrt(1 - y * y);
        CGFloat p3 = i * p1;
        CGFloat x = cos(p3) * r;
        CGFloat z = sin(p3) * r;
        
        
        DBPoint point = DBPointMake(x, y, z);
        NSValue *value = [NSValue value:&point withObjCType:@encode(DBPoint)];
        [coordinate addObject:value];
        
        CGFloat time = (arc4random() % 10 + 10.) / 20.;
        [UIView animateWithDuration:time delay:0. options:UIViewAnimationOptionCurveEaseOut animations:^{
            [self setTagOfPoint:point andIndex:i];
        } completion:^(BOOL finished) {
            
        }];
        
    }
 
    NSInteger a =  arc4random() % 10 - 5;
    NSInteger b =  arc4random() % 10 - 5;
    normalDirection = DBPointMake(a, b, 0);
    [self timerStart];
}

#pragma mark - set frame of point

- (void)updateFrameOfPoint:(NSInteger)index direction:(DBPoint)direction andAngle:(CGFloat)angle
{

    NSValue *value = [coordinate objectAtIndex:index];
    DBPoint point;
    [value getValue:&point];
    
    DBPoint rPoint = DBPointMakeRotation(point, direction, angle);
    value = [NSValue value:&rPoint withObjCType:@encode(DBPoint)];
    coordinate[index] = value;
    
    [self setTagOfPoint:rPoint andIndex:index];

}

- (void)setTagOfPoint: (DBPoint)point andIndex:(NSInteger)index
{
    UIView *view = [tags objectAtIndex:index];
    view.center = CGPointMake((point.x + 1) * (self.frame.size.width / 2.), (point.y + 1) * self.frame.size.width / 2.);
    
    CGFloat transform = (point.z + 2) / 3;
    view.transform = CGAffineTransformScale(CGAffineTransformIdentity, transform, transform);
    view.layer.zPosition = transform;
    view.alpha = transform;
    if (point.z < 0) {
        view.userInteractionEnabled = NO;
    }else {
        view.userInteractionEnabled = YES;
    }
}

#pragma mark - autoTurnRotation

- (void)timerStart
{
    timer.paused = NO;
}

- (void)timerStop
{
    timer.paused = YES;
}

- (void)autoTurnRotation
{
    for (NSInteger i = 0; i < tags.count; i ++) {
        [self updateFrameOfPoint:i direction:normalDirection andAngle:0.002];
    }
    
}

#pragma mark - inertia

- (void)inertiaStart
{
    [self timerStop];
    inertia.paused = NO;
}

- (void)inertiaStop
{
    [self timerStart];
    inertia.paused = YES;
}

- (void)inertiaStep
{
    if (velocity <= 0) {
        [self inertiaStop];
    }else {
        velocity -= 70.;
        CGFloat angle = velocity / self.frame.size.width * 2. * inertia.duration;
        for (NSInteger i = 0; i < tags.count; i ++) {
            [self updateFrameOfPoint:i direction:normalDirection andAngle:angle];
        }
    }
    
}

#pragma mark - gesture selector

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
    if (gesture.state == UIGestureRecognizerStateBegan) {
        last = [gesture locationInView:self];
        [self timerStop];
        [self inertiaStop];
        
    }else if (gesture.state == UIGestureRecognizerStateChanged) {
        CGPoint current = [gesture locationInView:self];
        DBPoint direction = DBPointMake(last.y - current.y, current.x - last.x, 0);
        
        CGFloat distance = sqrt(direction.x * direction.x + direction.y * direction.y);
        
        CGFloat angle = distance / (self.frame.size.width / 2.);
        
        for (NSInteger i = 0; i < tags.count; i ++) {
            [self updateFrameOfPoint:i direction:direction andAngle:angle];
        }
        normalDirection = direction;
        last = current;
    }else if (gesture.state == UIGestureRecognizerStateEnded) {
        CGPoint velocityP = [gesture velocityInView:self];
        velocity = sqrt(velocityP.x * velocityP.x + velocityP.y * velocityP.y);
        [self inertiaStart];
    }
}

@end
*/

